﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.Win32;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using EnvDTE;
using EnvDTE80;
using EnvDTE100;
using Microsoft.VisualStudio.VCProjectEngine;
using System.IO;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.TextManager.Interop;

// really just an implementation of the header switch macro
// from this page:
// http://www.gamedev.net/page/resources/_/reference/programming/general-programming/226/visual-studio-macros-for-the-game-programmer-r2434
// i had some hopes of making it smarter -- searching through
// include directories and project files and so on for
// additional partner file candidates, but, figuring out how to
// get that sort of information is proving difficult.
// 

namespace sven.HeaderSwitch
{

    [PackageRegistration(UseManagedResourcesOnly = true)]
    // This attribute is used to register the informations needed to show the this package
    // in the Help/About dialog of Visual Studio.
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    // This attribute is needed to let the shell know that this package exposes some menus.
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [Guid(GuidList.guidHeaderSwitchPkgString)]
    public sealed class HeaderSwitchPackage : Package
    {

        public HeaderSwitchPackage()
        {

        }

        protected override void Initialize()
        {

            base.Initialize();

            // Add our command handlers for menu (commands must exist in the .vsct file)
            OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if ( null != mcs )
            {
                // Create the command for the menu item.
                CommandID menuCommandID = new CommandID(GuidList.guidHeaderSwitchCmdSet, (int)PkgCmdIDList.cmdidViewHeaderSource);
                MenuCommand menuItem = new MenuCommand(ViewHeaderOrSource, menuCommandID);
                mcs.AddCommand( menuItem );
            
                // add a shortcut
                dte = (DTE)GetService(typeof(DTE));
                Commands cmds=dte.Commands;
                Command cmd;
                string cname = menuItem.ToString();

                cmd = cmds.Item("Tools.ViewHeaderOrSource");

                cmd.Bindings = "Text Editor::ctrl+shift+down arrow";
            }
        }

        DTE dte;



            /*

//Projects vcprojects = dte.GetObject("VCProjects") as VCProjects;

string f = Path.ChangeExtension(fname, ext);
string fshort = Path.GetFileName(f);

foreach (Project pr in dte.Solution.Projects)
{

    foreach (ProjectItem it in pr.ProjectItems)
    {
        for (short i = 1; i <= it.FileCount; i++)
        {

            string name = it.FileNames[i];

//                        foreach (string name in it.FileNames[i] )
//                      {
                if (Path.GetFileName(name) == fshort)
                {
                    return true;
                }
//                       }
        }
    }
}
            


return false;

            


foreach (Project pr in dte.Solution.Projects)
{

    string cpp = typeof(VCProject).GUID.ToString();

    if (pr.Kind != cpp) continue;
                        

    VCProject p = pr as VCProject;
    IVCCollection configs = (IVCCollection)p.Configurations;

    foreach (VCConfiguration cfg in configs)
    {
        IVCCollection tools = (IVCCollection)cfg.Tools;
        VCCLCompilerTool compilerTool = (VCCLCompilerTool)tools.Item("VCCLCompilerTool");
        string additional = compilerTool.AdditionalIncludeDirectories;

 

        VCProjectEngine vcProjEngine = (VCProjectEngine)compilerTool.VCProjectEngine;
        IVCCollection coll = (IVCCollection)vcProjEngine.Platforms;
        VCPlatform platForm = (VCPlatform)coll.Item("Win32");
        string include1 = platForm.Evaluate(platForm.IncludeDirectories);


        string include2 = platForm.Evaluate(additional);

    }

   // Microsoft.VisualStudio.VCProjectEngine engine = p.
                
}

 */

        // these try_open functions are just a version of Pierre
        // Arnaud's vb code, extended to iterate over all projects in
        // the solution:
        // http://www.codeproject.com/KB/macros/h_cpp_switcher.aspx
        //


        bool try_open_in_project(string file, ProjectItems pritems)
        {
            foreach (ProjectItem item in pritems)
            {


                /*
                if (item.SubProject!=null)
                {
                    if (try_open_in_project(file, item.SubProject.ProjectItems)) return true;
                }*/

                if (item.Name.ToLower() == file.ToLower())
                {
                    if (item.FileCount != 1) continue;
                   
                   // if (item.FileNames == null) continue;
                    string f = item.get_FileNames(0);
                    // string f = item.FileNames[0];
                    Window win = dte.ItemOperations.OpenFile(f);
                    win.Activate();

                    return true;
                }

                // item.ProjectItems
                if (item.ProjectItems != null && item.ProjectItems.Count > 0)
                {
                    if (try_open_in_project(file, item.ProjectItems)) return true;
                }

            }
            return false;
        }

        bool try_open_anywhere(string file)
        {

           Solution2 solution = (Solution2)dte.Solution;

//            foreach (Project pr in dte.Solution)
            foreach (Project pr in solution.Projects)
            {
                if (try_open_in_project(file, pr.ProjectItems))
                {
                    return true;
                }
            }
            return false;
        }



        bool try_open(string fname, string ext)
        {

            string f = Path.ChangeExtension(fname,ext);
            String basename = Path.GetFileName(f);

            //
            // eventually, i may add some logic to search through all
            // plausible include paths as well.
            //

            // steal the Edit.GotoDeclaration trick from:
            // http://www.gamedev.net/page/resources/_/reference/programming/general-programming/226/visual-studio-macros-for-the-game-programmer-r2434




            if (File.Exists(f)) {
                Window win = dte.ItemOperations.OpenFile(f);
                win.Activate();
                return true;
            }

            

            foreach (Window w in dte.Windows)
            {
                if (w.Caption.ToLower() == basename.ToLower())
                {
                    w.Activate();
                    return true;
                }
            }

            if (try_open_anywhere(Path.GetFileName(f))) return true;

            if (ext == ".h")
            {
                

                //dte.ActiveDocument.Activate();

                // i can't figure out how to get the Edit.GoToDeclaration 
                // command to be avaliable -- through, apparently, it is 
                // avaliable in visual basic macros :-P
                Commands cmds = dte.Commands;
                Command cmdobj = cmds.Item("Edit.GoToDeclaration");

                if (cmdobj.IsAvailable)
                {
                    dte.ExecuteCommand("Edit.GoToDeclaration", basename);
                   // cmds.Raise(cmdobj.Guid, cmdobj.ID, ref cin, ref cout);
                }

            }


            return false;
 
        }


        /// <summary>
        /// This function is the callback used to execute a command when the a menu item is clicked.
        /// See the Initialize method to see how the menu item is associated to this function using
        /// the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void ViewHeaderOrSource(object sender, EventArgs e)
        {

            

            dte = (DTE)GetService(typeof(DTE));
            Document doc = dte.ActiveDocument;
            if (doc == null) return;

            string fname = doc.FullName.ToLower();

            string [] headers = { ".h", ".hpp" };
            string [] sources = { ".c", ".cpp", ".cxx" };

            string ext = Path.GetExtension(fname);

            foreach (string h in headers)
            {
                if (ext == h) foreach (string c in sources)
                {
                    if (try_open(fname, c)) return;
                }
            }
            foreach (string c in sources)
            {

                if (ext == c)
                {
                    //open lua files over headers from the source
                    if (try_open(fname, ".lua")) return;

                    foreach (string h in headers)
                    {
                        if (try_open(fname, h)) return;
                    }

                }
            }
            if (ext == ".lua")
            {
                foreach (string h in headers)
                {
                    if (try_open(fname, h)) return;
                }
                foreach (string c in sources)
                {
                    if (try_open(fname, c)) return;
                }

            }
        }

    }
}
